# This is a little script that will generate promotional parts for
# your articles on LBRY network. Using Markdown.

# This script is under the terms of the GNU General Public License
# version 3 or any later version. (c) J.Y.Amihud 2021.

             ############# DEPENDENCIES #############

# This is not a software project intended for any-who. It's interned
# for the advanced user. And thus I will not provide a script to
# install dependecies. Mainly because I'm lazy. You can pull-request
# me one.

# This will need:

#         lbrynet executable. That you can either on:
#         https://github.com/lbryio/lbry-sdk/releases
#         or in the FastLBRY project:
#         https://notabug.org/jyamihud/FastLBRY-terminal

# The creation of images will be done using:

#         cairo
#
# And also the following imports should all be present on your system's
# python installation.

           #################  CODE ##################

import os
import sys     # 'python3 run.py -t' will activate test mode
import json
import math
import cairo
import random
import urllib.request
import urllib3
import subprocess
from PIL import Image

def comment_request(method: str, params: dict, addition=""):

    # Part of FastLBRY project
    
    """
    Sends a request to the comment API
    """
    data = {
        "method": method,
        "id": 1,
        "jsonrpc":"2.0",
        "params": params
    }
    data = json.dumps(data).encode()

    headers = {
        "Content-Type": "application/json"
    }

    
    
    try:
        req = urllib.request.Request(comments_api, data, headers)
        res = urllib.request.urlopen(req)
        out = res.read().decode()
        return json.loads(out)
    except Exception as e:
        print("COMMENT ERROR:", e)
        return

# The LBRY SDK reads your account data from ~/.config/lbry folder
# which is shared with LBRY Desktop and other LBRY based apps. So
# you will need to login from it.

# Also, the SDK should be already started. The script is
# dumb and will not check for it. You will need to use

#      lbrynet start

# Before running this script.

# Here put the full file_path of the SDK on your system.
SDK = "/home/vcs/Software/FastLBRY-GTK/flbry/lbrynet"
comments_api = "https://comments.odysee.com/api/v2"

# Let's get the latest publication's claim_id so we could fetch it's
# comments.

# HOW MANY PUBLICATIONS TO FETCH?

DATA_C = [] # This data will be populated with comments Data, so later
            # I could draw an image from it.

            
visited = [] # In case you've edited a publication. 
amount = ""
while True:
    amount = input("HOW MANY RECENT PUBLICATIONS TO LOAD? : ")
    try:
        amount = int(amount)
        break
    except:
        print("AMOUNT SHOULD BE A NUMBER!")

lbc = ""
while True:
    lbc = input("THE SUPPORT SHOULD BE ABOVE? : ")
    try:
        lbc = int(lbc)
        break
    except:
        print("IT SHOULD BE A NUMBER!")

fetch = ""
while True:
    fetch = input("HOW MANY COMMENTS TO FETCH FROM A PUBLICATION? : ")
    try:
        fetch = int(fetch)
        break
    except:
        print("IT SHOULD BE A NUMBER!")

print("FETCHING THE CLAIM_ID FOR THE LAST "+str(amount)+" PUBLICATIONS ...")

out = subprocess.check_output([SDK, "stream", "list",
                         '--page=1',         # FIRST PUBLICATION
                         '--page_size='+str(amount),    # LOAD SELECTED AMOUNT
                            "--no_totals"])

# Now let's convert what ever we received into a good data

print("READING THE DATA RECEIVED ...")
l = json.loads(out)
for out in l["items"]:

    if out["permanent_url"] in visited:
        continue
    else:
        visited.append(out["permanent_url"])
        
    # Let's give a user a little soothing information about what was
    # actually received.

    try:
        print("PUBLICATION: "+out["value"]["title"])
    except:
        print("PUBLICATION: "+out["permanent_url"])

    # Fetching the transactions of the publication

    print("FETCHING COMMNET-LESS SUPPORT ...")
    txo = subprocess.check_output([SDK,
                         "txo", "list", '--claim_id='+out["claim_id"],
                         '--page=1',
                         '--page_size='+str(fetch) # AMOUNT OF TXO TO LOAD
                                        ])
    txo = json.loads(txo)

    # This is a bit hacky, but we all love hacking so...
    # basically in each Tip for a publication you can see
    # a channel's claim_id.
    # Often it's 7d0b0f83a195fd1278e1944ddda4cda8d9c01a56
    # but this claim_id belongs to LBRY Fund channel that
    # does tipping based on views.

    # We want to see anything but this channel.

    for t in txo["items"]:
        if "signing_channel" in t and float(t["amount"]) > lbc:
            try:
                if t["signing_channel"]["channel_id"] != "7d0b0f83a195fd1278e1944ddda4cda8d9c01a56":

                    # This means we found a support

                    print("FOUND SUPPORT OF "+str(t["amount"]))

                    # I found that LBRY doesn't give a damn about the name
                    # if you have the claim_id.

                    print("FETCHING DATA ABOUT SENDER ...")
                    sender =  subprocess.check_output([SDK,
                                                       "claim","search", "--claim_id="+t["signing_channel"]["channel_id"]])
                    sender = json.loads(sender)
                    sender = sender["items"][0]
                    #print(t)
                    #sender = sender["a#"+t["signing_channel"]["channel_id"]]
                    
                    icon = ""
                    banner = ""
                    title = ""

                    try:
                        title = sender["name"]
                    except:
                        pass
                
                    
                    try:
                        icon = sender["value"]["thumbnail"]["url"]
                    except:
                        pass
                    try:
                        banner = sender["value"]["cover"]["url"]
                    except:
                        pass
                    try:
                        title = sender["value"]["title"]
                    except:
                        pass

                    try:
                        link = sender["canonical_url"].replace("#",":").replace("lbry://", "")
                    except:
                        link = ""

                    url_of_comment = out["permanent_url"].replace("#",":").replace("lbry://", "https://odysee.com/")

                    print("SENDER NAME: "+title)
                    print("SENDER BANNER: "+banner)
                    print("SENDER ICON: "+icon)
                    print("SENDER LINK: "+link)
                    print("COMMENT LINK: "+url_of_comment)

                    DATA_C.append([
                        float(t["amount"]),  # 0
                        title,               # 1
                        "",                  # 2
                        icon,                # 3
                        banner,              # 4
                        link,                # 5
                        url_of_comment       # 6

                    ])
            except:
                pass

    # Fetching comments from the publication

    print("FETCHING COMMENTS ...")

    # Old code, no longer supported by the new SDK
    
    # comments = subprocess.check_output([SDK,
    #                      "comment", "list", out["claim_id"],
    #                      '--page=1',
    #                      '--page_size='+str(fetch), # AMOUNT OF COMMENTS TO LOAD
    #                                     '--include_replies'])

    params = {
        "claim_id": out["claim_id"],
        "page": 1,
        "page_size": fetch,
        "sort_by": 3,
        "top_level": False,
        }
    
    comments = comment_request("comment.List", params)["result"]
    # Trying to read the list

    # comments = json.loads(comments)

    print( str(comments["total_items"])+" COMMENTS FOUND" )

    if 'items' in comments:
        for c in comments["items"]:

            # Each comment will be checked for the amount
            if c["support_amount"] > lbc:

                # So we found a supporter comment
                try:
                    print(c["channel_name"], "SUPPORT: "+str(c["support_amount"]),
                        "["+c["comment"][:20]+"...]")
                except:
                    print("ANONYMOUS", "SUPPORT: "+str(c["support_amount"]),
                        "["+c["comment"][:20]+"...]")
                    
                # We need to get the image of the channel.
                # and make a pretty picture for this comment.

                icon = ""
                banner = ""
                title = c["channel_name"]
                
                
                if "channel_id" in c:
                    print("FETCHING DATA ABOUT SENDER ...")
                    sender =  subprocess.check_output([SDK,
                         "resolve", c["channel_name"]+"#"+c["channel_id"]])
                    sender = json.loads(sender)
                    sender = sender[c["channel_name"]+"#"+c["channel_id"]]
                    try:
                        icon = sender["value"]["thumbnail"]["url"]
                    except:
                        pass
                    try:
                        banner = sender["value"]["cover"]["url"]
                    except:
                        pass
                    try:
                        title = sender["value"]["title"]
                    except:
                        pass
                try:
                    link = sender["canonical_url"].replace("#",":").replace("lbry://", "")
                except:
                    link = ""
                    
                url_of_comment = out["permanent_url"].replace("#",":").replace("lbry://", "https://odysee.com/")\
                    +"?lc="+c["comment_id"]
                
                print("SENDER NAME: "+title)
                print("SENDER BANNER: "+banner)
                print("SENDER ICON: "+icon)
                print("SENDER LINK: "+link)
                print("COMMENT LINK: "+url_of_comment)

                # Because we checked earlier for the support not related
                # to the comments. The supported comment ( if chose to filter
                # by more then 0 LBC ) will be already added but without the
                # comments string. So we need to delete it first.

                found = False
                for item in DATA_C:
                    if c["support_amount"] in item\
                    and title in item\
                    and icon in item\
                    and banner in item\
                    and out["permanent_url"].replace("#",":").replace("lbry://", "https://odysee.com/") in item\
                    and link in item:
                        found = item
                        break

                if found:
                    DATA_C.remove(found)
                
                DATA_C.append([
                    c["support_amount"], # 0
                    title,               # 1
                    c["comment"],        # 2
                    icon,                # 3
                    banner,              # 4
                    link,                # 5
                    url_of_comment       # 6
                    
                ])


# Now that we have all the comments ready in the DATA_C list
# we can start drawing the comments.

# The idea is to have a .md formatted text that you can add in
# the end of the article. With images uploaded and ready to go.


def roundrect(layer, x, y, width, height, r):

    # This function will trace a round rectangle
    
    if width < r*2:
        width = r*2
    if height < r*2:
        height = r*2
    
    layer.move_to(x,y+r)
    layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
    layer.arc(x+width-r, y+r, r, 3*math.pi/2, 0)
    layer.arc(x+width-r, y+height-r, r, 0, math.pi/2)
    layer.arc(x+r, y+height-r, r, math.pi/2, math.pi)
    layer.close_path()


def get_image(url, save_url):

    # This function will download an image and save it
    # as a PNG image
    
    filename = "images/"+str(save_url)

    # First let's download the image
    
    http = urllib3.PoolManager()
    r = http.request('GET', url, preload_content=False)

    with open(filename, 'wb') as out:
        while True:
            data = r.read(1024)
            if not data:
                break
            out.write(data)

    r.release_conn()

    # Then converting to png

    im = Image.open(filename)
    im.save(filename+".png")

    # REMOVING THE TEMPORARY ONE
    os.remove(filename)
    
def fit(load2, width, height):

    # This will scale a layer to fit into an
    # area.

    newimage = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    imagedraw = cairo.Context(newimage)
    
    
    factor = 1
    if (load2.get_height()*(width/load2.get_width()))\
        < height:
        factor = height / load2.get_height()
    else:
        factor = width / load2.get_width()
    #factor = 0.1
    imagedraw.scale(factor, factor)    
    dx = (width/2)/factor -(load2.get_width() /2)
    dy = (height/2)/factor -(load2.get_height()/2)

    imagedraw.set_source_surface(load2, dx, dy)    
    imagedraw.paint()
    return newimage


MD = "# Supporters:\n\n"
MD = MD + "*Showing hyper-comments and other support with more than "+str(lbc)+" LBC from the last "+str(amount)+" publications. "
MD = MD + "Including publications by other channels of my account. It's done automatically using* [this](https://notabug.org/jyamihud/LBRY-Supported-Comments) *script.*\n\n"

for n, c in enumerate(reversed(sorted(DATA_C))):


    print("DRAWING IMAGE FOR COMMENT NUMBER", n)
    
    image_width = 1100
    image_height = 200
    
    # Now let's make the cairo object

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, image_width, image_height)
    layer = cairo.Context(surface)
    layer.select_font_face("Dyuthi", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)

    
    # BACKGROUND 
    roundrect(layer, 0, 0, image_width, image_height, 50)
    layer.set_source_rgba(0.21,0.22,0.29,1) # BACKGROUND IS #353849
    layer.fill()

    # AROUND THING 
    roundrect(layer, 10, 10, image_width-20, image_height-20, 40)
    layer.set_line_width(20)
    layer.set_source_rgba(1,0.72,0,1) # YELLOW THING AROUND
    layer.stroke()

    # CLIP 1

    roundrect(layer, 20, 20, image_width-40, image_height-40, 30)
    layer.clip()
    
    # Banner
    try:
        if c[4]:
            get_image(c[4], str(n)+"banner")
            banner = cairo.ImageSurface.create_from_png("images/"+str(n)+"banner.png")
            banner = fit(banner, image_width-40, image_height-40)
            layer.set_source_surface(banner, 20, 20)
            layer.paint()

            # REMOVING THE BANNER ORIGNINAL
            os.remove("images/"+str(n)+"banner.png")
    except:
        pass
    

    # MAKING BANNER LESS VISIBLE 
    roundrect(layer, 0, 0, image_width, image_height, 50)
    layer.set_source_rgba(0.21,0.22,0.29,0.75) 
    layer.fill()

    

    
    #NAME OF THE CHANNEL
    layer.set_source_rgba(1,0.72,0,1) # YELLOW THING AROUND
    layer.set_font_size(60)
    layer.move_to(210,80)
    if c[1]:
        layer.show_text(c[1])
    else:
        layer.set_font_size(30)
        layer.show_text("Help... For some reason I can't get the channel resolved. Line: 149")
        # TODO: See the message on top. It's something to do with
        #       line 149. Please help. I can't understand the problem.
        #       It used to work. But no longer works.
        
        c[1] = "[Channel Name]"

    #LBC AMOUNT

    lbc = cairo.ImageSurface.create_from_png("images/lbc.png")
    layer.set_source_surface(lbc, 210, 110)
    layer.paint()
    
    layer.set_source_rgba(1,1,1,1) # WHITE
    layer.set_font_size(55)
    layer.move_to(260,150)
    layer.show_text(str(c[0]))

    # COMMENTED WITH:
    if c[2]:
        layer.set_source_rgba(1,0.72,0,1) # YELLOW THING AROUND
        layer.set_font_size(30)
        layer.move_to(800,150)
        layer.show_text("Commented with:")


    # CLIP 2 ICON
    roundrect(layer, 25, 25, 150, 150, 25)
    layer.clip()
    
    # Icon
    if c[3]:
        try:
            get_image(c[3], str(n)+"icon")
            icon = cairo.ImageSurface.create_from_png("images/"+str(n)+"icon.png")
            icon = fit(icon, 150, 150)
            layer.set_source_surface(icon, 25, 25)
            layer.paint()

            # REMOVING THE BANNER ORIGNINAL
            os.remove("images/"+str(n)+"icon.png")
        except:
            print("Icon Failed!")
        
    # SAVE TO IMAGE

    print("SAVING IMAGE ...")
    try:
        os.mkdir("images")
    except:
        pass
    surface.write_to_png("images/"+str(n)+".png")
    print("SAVED AS: images/"+str(n)+".png")

    # Uploading the IMAGE to the LBRY network
    # We gonna need the images to be online

    if not "-t" in sys.argv:
    
        print("UPLOADING IMAGE: images/"+str(n)+".png TO LBRY ...")

        lbryname = ""
        good = "qwertyuiopasdfghjklzxcvbnm-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
        for i in range(30):
            lbryname = lbryname + random.choice(good)


        img = subprocess.check_output([
            SDK,
            "publish",
            "--name="+lbryname,
            "--bid=0.001",
            "--file_path="+os.getcwd()+"/images/"+str(n)+".png"])
        img = json.loads(img)
        imageurl = img['outputs'][0]['permanent_url'].replace("lbry://","https://spee.ch/")

        print("PUBLISHED : "+imageurl)

    else:
        # If testing mode
        imageurl = "images/"+str(n)+".png"
        
    # Adding the comment to the MD file
    
    MD = MD + "[![]("+imageurl+")]("+c[6]+")\n"

    if c[2]: # If there are any comments
        for line in c[2].split("\n"):

            bad_site = False
            
            # This feature will implement a redirect
            # or warning if a sender wants to link to
            # a website which are known to be nasty.

            filt = {
                "youtube.com/":"yewtu.be/",
                "youtu.be/":"yewtu.be/",
                "twitter.com/":"nitter.net/",
                "reddit.com/":"teddit.net/"
            }

            for site in filt:
                if site in line:
                    line = line.replace(site, filt[site])
                    bad_site = True
                    
            
            MD = MD + "> "+line+"\n"

            if bad_site:
                MD = MD + "\n\n*This line included links to unsafe and non-free internet dis-services. The link was automatically edited to load a Free / Private alternative front end.*\n"

    MD = MD + "\n\n*I want to show appreciation to ["+c[1]+"](https://odysee.com/"+c[5]+") for supporting me with "+str(c[0])+" LBC coins. "
    MD = MD + "You can send some of your LBC to ["+c[1]+"](https://odysee.com/"+c[5]+") or follow ["+c[1]+"](https://odysee.com/"+c[5]+") to return the favor.*"
        
    MD = MD + "\n\n\n__________\n"
    
print("----------- THE MD IS -------------")
print(MD)
print("-------------THE END---------------")

savemd = open("output.md", "w")
savemd.write(MD)
savemd.close()

print("SAVED AS: output.md")

# Show preview if it's a test.
if "-t" in sys.argv:
    os.system("xdg-open output.md")
